package lab1_ArrayList;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;

/**
 * ArrayList - List based on Array with iterator and methods:
 * add, reSize, clear, isEmpty, removeObject, removeByIndex, size, get, toString
 * 
 * @author Denis Fedotov
 */

public class MyArrayList<T> implements List<T> {

	private T[] mas;
	private int countMas;
	
	
	/**
	 * Constructor of ArrayList
	 */
	
	@SuppressWarnings("unchecked")
	public MyArrayList() {
		this.mas= (T[]) new Object[7];                
		this.countMas=0;                               
	}
	
	/**
	 * Add element to ArrayList
	 * Works at worst in time O(countMas)
	 * 
	 * @param e <Element to add>
	 * @return <True if element was added or false if element could not be added>
	 */
	
	@Override
	public boolean add(T e) {
		if (this.mas.length==this.countMas) {
			if (!reSize()) {
				return false;			        
			}
		}
		this.mas[this.countMas]=e;                  
		countMas++;
		return true;
	}

	/**
	 * Increase size of ArrayList
	 * Works at worst in time O(countMas)
	 * 
	 * @return <True if size was increased>
	 */
	
	public boolean reSize() {
		@SuppressWarnings("unchecked")
		T[] bigMas=(T[]) new Object[2*this.mas.length];   
		for (int i=0;i<this.mas.length;i++)               
		{
			bigMas[i]=this.mas[i];                     
		}
		this.mas=bigMas;				
		return true;
	}

	/**
	 * Clear ArrayList
	 * Works in time O(countMas)
	 */
	
	@Override
	public void clear() {
		for (int i=0; i<countMas;i++)
		{
			this.mas[i]=null;
		}
		countMas=0;
	}

	/**
	 * Check ArrayList for emptiness
	 * Works in time O(1)
	 * @return <True if ArrayList is empty or false if is not empty>
	 */
	
	@Override
	public boolean isEmpty() {
		if (countMas==0) return true;
		else return false;
	}
	
	/**
	 * Remove element from ArrayList
	 * Works in time O(countMas)
	 * @param o <Element to remove>
	 * @return <True if element was removed or false if element could not be removed>
	 */
	
	public boolean removeObject(T o) {
		
		for (int i=0;i<countMas;i++)
		{
			if (o.equals(this.mas[i])) 
			{
				countMas--;
				for (int y=i;y<countMas;y++)
					this.mas[y]=this.mas[y+1];
				this.mas[countMas]=null;
				return true;
			}
		}
		return false;
	}
	
	/**
	 * Show size of ArrayList
	 * Works in time O(1)
	 * @return <Size (as number) of ArrayList>
	 */
	
	@Override
	public int size() {
		return countMas;
	}
	
	/**
	 * Remove element by index from ArrayList
	 * Works in time O(countMas-index)
	 * @param index <Index (as number) of element to remove>
	 * @return <Element that was removed or null if element was not found>
	 * @throws IndexOutOfBoundsException <If index is outside of borders>
	 */
	
	public T removeByIndex(int index) {
		if ((index < 0)||(index >= this.countMas)) throw new IndexOutOfBoundsException("Index is out of bounds");
		if(index < countMas){
			T rem=this.mas[index];
			countMas--;
			for (int y=index;y<countMas;y++)
				this.mas[y]=this.mas[y+1];
			this.mas[countMas]=null;
			return rem;
		}
		return null;
	}

	/**
	 * Get element from ArrayList
	 * Works in time O(1)
	 * @param index <Index (as number) of element to get>
	 * @return <Element that belongs to this index>
	 * @throws IndexOutOfBoundsException <If index is outside of borders>
	 */
	
	@Override
	public T get(int index) {
		if ((index < 0)||(index >= this.countMas)) throw new IndexOutOfBoundsException("Index is out of bounds");
		return this.mas[index];
	}
	
	/**
	 * Turn ArrayList to string
	 * Works in time O(countMas)
	 * @return <String with elements of ArrayList>
	 */
	
	public String toString(){
		String str="";
		for(int i=0; i<countMas-1;i++){
			str=str+mas[i]+" ";
		}
		str=str+mas[countMas-1];
		return str;
	}

	/**
	 * Iterator of ArrayList
	 */
	
	@Override
	public ListIterator<T> listIterator() {
		return new ListIterator<T>() {

			private int cursor=-1;
			
			/**
			 * Get next element from ArrayList
			 * Works in time O(1)
			 * @return <Next element>
			 * @throws NoSuchElementException <If we are at the end  of ArrayList>
			 */
			
			@Override
			public T next() {
				if (!hasNext()) throw new NoSuchElementException("End of ArrayList");
				if ((cursor>=-1)&&(cursor<countMas-1)) return mas[++cursor];
				else throw new IndexOutOfBoundsException();
			}
			
			/**
			 * Get previous element from ArrayList
			 * Works in time O(1)
			 * @return <Previous element>
			 * @throws NoSuchElementException <If we are at the start of ArrayList>
			 */
			
			@Override
			public T previous() {
				if (!hasPrevious()) throw new NoSuchElementException("Start of ArrayList");
				if ((cursor>=0)&&(cursor<countMas)) return mas[cursor--];
				else throw new IndexOutOfBoundsException();
			}
			
			/**
			 * Check ArrayList for next element
			 * Works in time O(1)
			 * @return <True if next element exists or false if not exists>
			 */
			
			@Override
			public boolean hasNext() {
				if ((cursor>=-1)&&(cursor<countMas-1)) return true;
				else return false;
			}
			

			/**
			 * Check ArrayList for previous element
			 * Works in time O(1)
			 * @return <True if previous element exists or false if not exists>
			 */

			@Override
			public boolean hasPrevious() {
				if ((cursor>=0)&&(cursor<=countMas-1)) return true;
				else return false;
			}
			
			@Override
			public void add(T e) {
				throw new UnsupportedOperationException("Not supported yet.");
			}

			@Override
			public int nextIndex() {
				throw new UnsupportedOperationException("Not supported yet.");
			}

			@Override
			public int previousIndex() {
				throw new UnsupportedOperationException("Not supported yet.");
			}

			@Override
			public void remove() {
				throw new UnsupportedOperationException("Not supported yet.");
			}

			@Override
			public void set(T e) {
				throw new UnsupportedOperationException("Not supported yet.");
			}
			
		};
	}
	
	@Override
	public T remove(int index) {
		return removeByIndex(index);
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public boolean remove(Object o) {
		return removeObject((T)o);
	}
	
	@Override
	public Iterator<T> iterator() {
		return listIterator();
	}

	@Override
	public List<T> subList(int fromIndex, int toIndex) {
		throw new UnsupportedOperationException("Not supported yet.");
	}

	@Override
	public Object[] toArray() {
		throw new UnsupportedOperationException("Not supported yet.");
	}

	@SuppressWarnings("hiding")
	@Override
	public <T> T[] toArray(T[] arg0) {
		throw new UnsupportedOperationException("Not supported yet.");
	}
	
	@Override
	public void add(int index, T element) {
		throw new UnsupportedOperationException("Not supported yet.");
	}
	
	@Override
	public boolean addAll(Collection<? extends T> c) {
		throw new UnsupportedOperationException("Not supported yet.");
	}

	@Override
	public boolean addAll(int index, Collection<? extends T> c) {
		throw new UnsupportedOperationException("Not supported yet.");
	}

	@Override
	public boolean contains(Object o) {
		throw new UnsupportedOperationException("Not supported yet.");
	}

	@Override
	public boolean containsAll(Collection<?> c) {
		throw new UnsupportedOperationException("Not supported yet.");
	}
	
	@Override
	public int indexOf(Object o) {
		throw new UnsupportedOperationException("Not supported yet.");
	}

	@Override
	public int lastIndexOf(Object o) {
		throw new UnsupportedOperationException("Not supported yet.");
	}
	
	@Override
	public ListIterator<T> listIterator(int index) {
		throw new UnsupportedOperationException("Not supported yet.");
	}
	
	@Override
	public boolean removeAll(Collection<?> c) {
		throw new UnsupportedOperationException("Not supported yet.");
	}

	@Override
	public boolean retainAll(Collection<?> c) {
		throw new UnsupportedOperationException("Not supported yet.");
	}

	@Override
	public T set(int index, T element) {
		throw new UnsupportedOperationException("Not supported yet.");
	}
}
